🆕 程式碼
/** 共用:把 style 要求整理成說明文字 */
function styleHint(style = "") {
if (!style) return "";
return 請遵循程式風格指引:「${style}」。若無法完全符合,請在說明段落中標註差異與原因。
;
}
/** 共用:把語言與框架組裝成上下文 */
function langFrameworkHint(language = "javascript", framework = "") {
const fw = framework ? ,測試框架傾向使用 ${framework}
: "";
return 主要語言:${language}${fw}。
;
}
/** A. 解釋程式碼 */
export async function explainCode(snippet, { language = "javascript" } = {}) {
if (!snippet?.trim()) throw new Error("請提供要解釋的程式碼。");
const pb = new PromptBuilder()
.setRole("你是資深軟體工程師與技術講師")
.setGoal("以條理清楚、層次分明的方式解釋程式碼")
.addConstraint("示範輸入/輸出與邊界案例")
.addConstraint("指出時間與空間複雜度(若適用)")
.addConstraint("避免過度口語化,重點使用條列")
.setFormatHint("使用 Markdown 呈現:背景、流程、資料結構、複雜度、邊界條件、示例")
.setUserInput(語言:${language}\n---\n${snippet}
);
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
temperature: 0.3,
messages: pb.toMessages(),
});
return res.choices?.[0]?.message?.content?.trim() ?? "";
}
/** B. 程式碼審查(安全/健全性/可維護)→ 回純 JSON */
export async function reviewCode(snippet, {
language = "javascript",
style = "",
} = {}) {
if (!snippet?.trim()) throw new Error("請提供要審查的程式碼。");
const schemaHint = 請以純 JSON 回覆,格式: { "summary": "概覽結論(不超過120字)", "risks": [ {"type":"security|reliability|performance|readability|maintainability|other","detail":"風險描述","evidence":"代碼片段或理由","severity":"low|medium|high","fix":"具體修正建議"} ], "complexity": {"time":"O(...)", "space":"O(...)", "explanation":"補充說明"}, "checklist": ["命名一致", "錯誤處理", "邊界檢查", "資源釋放", "日誌與追蹤"], "styleNotes": ["與 style 規範的差異與修正建議..."] }
;
const pb = new PromptBuilder()
.setRole("你是極度嚴謹的程式碼審查者與安全顧問")
.setGoal("以安全、穩定、效能、可維護角度審查程式碼並提出可執行的修正")
.addConstraint("不要虛構外部依賴;只根據提供的程式碼評估")
.addConstraint("必要時提出更穩健的 API 用法或邊界檢查")
.addConstraint("以 evidence 附上具體片段或線索")
.addConstraint(styleHint(style))
.setFormatHint(schemaHint)
.setUserInput(${langFrameworkHint(language)}\n---\n${snippet}
);
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
temperature: 0.2,
messages: pb.toMessages(),
});
const raw = res.choices?.[0]?.message?.content?.trim() ?? "{}";
const json = raw.match(/(?:json)?\s*([\s\S]*?)
/i)?.[1] ?? raw;
return JSON.parse(json);
}
/** C. 重構:純淨可貼上的重構版本 + 說明 */
export async function refactorCode(snippet, {
language = "javascript",
style = "",
goals = ["提升可讀性", "加入邊界檢查", "降低複雜度"],
} = {}) {
if (!snippet?.trim()) throw new Error("請提供要重構的程式碼。");
const pb = new PromptBuilder()
.setRole("你是注重可維護性的資深工程師")
.setGoal("在不改變對外行為與輸入輸出契約的前提下進行重構")
.addConstraint("若需要破壞式變更,需明確標註並給出遷移方案")
.addConstraint("保留註解與重要錯誤處理;補上缺漏的邊界檢查")
.addConstraint(styleHint(style))
.setFormatHint("輸出兩段:1) 重構後程式碼(使用正確語法標註語言);2) 說明變更與取捨")
.setUserInput(${langFrameworkHint(language)}\n重構目標:${goals.join("、")}\n---\n${snippet}
);
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
temperature: 0.25,
messages: pb.toMessages(),
});
return res.choices?.[0]?.message?.content?.trim() ?? "";
}
/** D. 產生單元測試草稿(JSON + 可貼上檔案) */
export async function generateTests(snippet, {
language = "javascript",
framework = "jest",
coverage = "medium", // minimal | medium | high
ioSignature = "", // 可選:函式簽名或 public API
} = {}) {
if (!snippet?.trim()) throw new Error("請提供要測試的程式碼。");
const schemaHint = 請以純 JSON 回覆,格式: { "framework": "${framework}", "files": [ {"path":"__tests__/module.spec.${language==='typescript'?'ts':'js'}","content":"<測試檔案完整內容>"} ], "cases": [ {"name":"基本案例","inputs":"...","expected":"...","notes":"涵蓋路徑、邊界、例外"} ], "gaps": ["目前無法自動覆蓋的邊角或需要人工模擬的外部資源..."] }
;
const pb = new PromptBuilder()
.setRole("你是資深測試工程師與 TDD 倡導者")
.setGoal("為給定程式碼產生可執行的單元測試草稿,覆蓋關鍵路徑與邊界")
.addConstraint("若存在外部 I/O 或隨機性,請使用 mock/stub/fake")
.addConstraint("測試需可獨立執行;避免依賴專案不存在的工具")
.addConstraint(覆蓋度偏好:${coverage}
)
.setFormatHint(schemaHint)
.setUserInput(${langFrameworkHint(language, framework)}\nAPI/簽名:${ioSignature || "(未提供)"}\n---\n${snippet}
);
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
temperature: 0.25,
messages: pb.toMessages(),
});
const raw = res.choices?.[0]?.message?.content?.trim() ?? "{}";
const json = raw.match(/(?:json)?\s*([\s\S]*?)
/i)?.[1] ?? raw;
return JSON.parse(json);
}
// ...既有 args 解析
async function main() {
const task = args.task || "chat";
if (task === "code") {
const mode = args.mode || "explain"; // explain | review | refactor | tests
const language = args.lang || "javascript";
const style = args.style || ""; // 例:"Airbnb JS Style" / "PEP8"
const framework = args.framework || "jest";
const coverage = args.coverage || "medium";
const ioSignature = args.signature || "";
// 讀入 snippet:優先 --file,再用 --code 文字
let snippet = args.code || "";
if (args.file) {
const fs = await import("fs");
snippet = fs.readFileSync(args.file, "utf-8");
}
if (!snippet.trim()) {
snippet = `function sum(arr){ let s=0; for (let i=0;i<arr.length;i++){ s+=arr[i] } return s }`;
}
if (mode === "explain") {
const out = await explainCode(snippet, { language });
console.log("\n=== 解釋程式碼 ===\n");
console.log(out);
} else if (mode === "review") {
const out = await reviewCode(snippet, { language, style });
console.log("\n=== 程式碼審查(JSON) ===\n");
console.log(JSON.stringify(out, null, 2));
} else if (mode === "refactor") {
const out = await refactorCode(snippet, { language, style });
console.log("\n=== 重構輸出 ===\n");
console.log(out);
} else if (mode === "tests") {
const out = await generateTests(snippet, { language, framework, coverage, ioSignature });
console.log("\n=== 測試草稿(JSON) ===\n");
console.log(JSON.stringify(out, null, 2));
} else {
console.log("未知模式,請使用 --mode explain | review | refactor | tests");
}
} else {
// ...你原本的其他 task 分支
}
}
main().catch((e) => {
console.error("發生錯誤:", e.message);
process.exit(1);
});
你可改 --lang typescript|python|java|go|rust 等,--framework 改成 vitest|mocha|pytest|junit 皆可。
若不想放檔案,改用 --code "" 即可。
▶️ 使用示例(CLI)
npm run day13:explain --silent
npm run day13:review --silent
npm run day13:refactor --silent
npm run day13:tests --silent